﻿using FreeRedis;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        //Redis分布式锁方案一：SETNX + EXPIRE

        //即先用setnx来抢锁，如果抢到之后，再用expire给锁设置一个过期时间，防止锁忘记了释放
        //SETNX 是SET IF NOT EXISTS的简写.日常命令格式是SETNX key value，如果 key不存在，则SETNX成功返回1，如果这个key已经存在了，则返回0
        //这个方案中，setnx和expire两个命令分开了，「不是原子操作」。
        //如果执行完setnx加锁，正要执行expire设置过期时间时，进程crash或者要重启维护了，那么这个锁就“长生不老”了，「别的线程永远获取不到锁啦」

        Console.WriteLine("请输入秒杀商品Id：");
        var skuId = Console.ReadLine();

        Console.WriteLine("请输入秒杀商品库存:");
        var skuStockNum = int.Parse(Console.ReadLine() ?? "10");
        RedisClient redisSkuStockClient = new RedisClient("127.0.0.1:32002,defaultDatabase=0");
        var skuStockKey = $"sku:stock:{skuId}";
        redisSkuStockClient.Set<int>(skuStockKey, skuStockNum);

        Console.WriteLine("请输入秒杀数量：");
        var skuNum = int.Parse(Console.ReadLine() ?? "1");

        Console.WriteLine("请输入秒杀并发人数：");
        var userCount = int.Parse(Console.ReadLine() ?? "100");

        var redisKey = $"sale:activity:{skuId}";
        for (int i = 1; i <= userCount; i++)
        {
            var userId = i;
            Thread thread = new Thread(new ThreadStart(() =>
            {
                RedisClient redisSetExClient = new RedisClient("127.0.0.1:32002,defaultDatabase=0");
                var param = new { skuId = skuId, SkuName = $"商品：{skuId}", count = skuNum, userId = userId };
                var paramValue = Newtonsoft.Json.JsonConvert.SerializeObject(param);

                var isLock = redisSetExClient.SetNx<string>(redisKey, paramValue, 10);
                if (isLock)
                {
                    redisSetExClient.SetEx(redisKey, 10, paramValue);
                    var skuStock = redisSetExClient.Get<int>(skuStockKey);
                    var newSkuStock = skuStock - skuNum;
                    if (newSkuStock >= 0)
                    {
                        Console.WriteLine($"用户:{userId}，已获取到锁，已生成订单，现库存：{skuStock},下单后库存：{newSkuStock},--------------------");
                        redisSkuStockClient.Set<int>(skuStockKey, newSkuStock);
                        redisSkuStockClient.Del(redisKey);
                    }
                    else
                    {
                        Console.WriteLine($"用户:{userId}，未生成订单，现库存：{skuStock}");
                    }
                }
                else
                {
                    Console.WriteLine($"用户:{userId}，未获取到锁");
                }
            }));
            thread.Start();
        }
    }
}